package com.managertrade.util.strategy;



import cn.hutool.core.date.DateUtil;

import java.math.BigDecimal;
import java.util.*;

import static com.managertrade.util.strategy.XgbKlinePipeline.DATA_DIR;

public class KlineToFundsBacktest {

    private static final BigDecimal INIT_FUNDS = BigDecimal.valueOf(5);

    // 示例的K线结构
    public static class KlineWithIndicators {
        private long timestamp;
        private double open;
        private double high;
        private double low;
        private double close;
        private double volume;

        public KlineWithIndicators(long timestamp, double open, double high, double low, double close, double volume) {
            this.timestamp = timestamp;
            this.open = open;
            this.high = high;
            this.low = low;
            this.close = close;
            this.volume = volume;
        }

        public double getOpen() { return open; }
        public double getClose() { return close; }
    }

    public static void main(String[] args) throws Exception {
        // 模拟几条K线数据
        List<com.managertrade.dto.vo.KlineWithIndicators> klines = ExcelKlineReader.readExcelToKlines(DATA_DIR + "/ASTER-USDT_1m_kline.xlsx");
        // 转换成资金曲线
        List<BigDecimal> fundsList = generateFundsCurve(klines);
        BigDecimal alpha = new BigDecimal("0.001");   // 起始值
        BigDecimal step = new BigDecimal("0.0005");    // 每次增加的步长
        BigDecimal max = new BigDecimal("0.1");      // 最大值

        // 使用前面的资金区间策略回测
        //FundsWindowBacktest.Result backtest = FundsWindowBacktest.backtest2(fundsList,alpha.doubleValue(),1,2);
        intervalStrategy(klines);
    }

    // ===== 计算 EMA =====
    public static void calculateEMA(List<com.managertrade.dto.vo.KlineWithIndicators> klines) {
        if (klines.size() < 21) return;

        double multiplier7 = 2.0 / (7 + 1);
        double multiplier21 = 2.0 / (21 + 1);

        // 初始化 EMA7 和 EMA21
        klines.get(6).setEma7(averageClose(klines.subList(0, 7)));
        klines.get(20).setEma21(averageClose(klines.subList(0, 21)));

        // 计算 EMA7
        for (int i = 7; i < klines.size(); i++) {
            double prevEma7 = (i == 7) ? klines.get(6).getEma7() : klines.get(i - 1).getEma7();
            double ema7 = (klines.get(i).getClose() - prevEma7) * multiplier7 + prevEma7;
            klines.get(i).setEma7(ema7);
        }

        // 计算 EMA21
        for (int i = 21; i < klines.size(); i++) {
            double prevEma21 = (i == 21) ? klines.get(20).getEma21() : klines.get(i - 1).getEma21();
            double ema21 = (klines.get(i).getClose() - prevEma21) * multiplier21 + prevEma21;
            klines.get(i).setEma21(ema21);
        }
    }
    /**
     * 回测 EMA 趋势准确率
     * @param klines 历史 K 线
     * @param futureWindow 未来 N 根 K 线来验证信号
     */
    public static void backtestEMA(List<com.managertrade.dto.vo.KlineWithIndicators> klines, int futureWindow) {
        if (klines.size() < 120) {
            System.out.println("K线数据太少，无法回测");
            return;
        }

        int rangePeriod = 100;
        int correct = 0;
        int total = 0;

        // 状态记录：上一次非 hold 的信号（"buy" 或 "sell"），以及该方向连续错误次数
        String lastSignal = null;
        int consecutiveWrongCount = 0;

        for (int i = rangePeriod; i < klines.size() - futureWindow; i++) {
            com.managertrade.dto.vo.KlineWithIndicators k = klines.get(i);

            // 最近100根区间（不包含当前）
            List<com.managertrade.dto.vo.KlineWithIndicators> rangeList = klines.subList(i - rangePeriod, i);
            double highest = rangeList.stream().mapToDouble(com.managertrade.dto.vo.KlineWithIndicators::getHigh).max().orElse(0);
            double lowest = rangeList.stream().mapToDouble(com.managertrade.dto.vo.KlineWithIndicators::getLow).min().orElse(0);
            double mid = (highest + lowest) / 2.0;

            double close = k.getClose();
            String signal;

            // ===== 原始策略逻辑 =====
            if (close > highest) {
                signal = "buy"; // 突破上轨 → 做多
            } else if (close < lowest) {
                signal = "sell"; // 跌破下轨 → 做空
            } else if (close >= mid && close <= highest) {
                signal = "hold"; // 上半区 → 观望
            } else { // close 在 mid 和 lowest 之间
                signal = "buy"; // 下半区 → 做多（区间反转）
            }

            // 忽略 hold
            if (signal.equals("hold")) {
                System.out.printf("时间:%s, 当前价:%.2f, 区间[%.2f~%.2f], 中间:%.2f, 信号:hold (跳过)%n",
                        k.getDatetime(), close, lowest, highest, mid);
                continue;
            }

            // ===== 如果同一方向连续错了两次及以上，则翻转方向（测试用） =====
            boolean flipped = false;
            String executedSignal = signal;
            if (lastSignal != null && consecutiveWrongCount >= 2 && signal.equals(lastSignal)) {
                // 翻转
                executedSignal = signal.equals("buy") ? "sell" : "buy";
                flipped = true;
            }

            // ===== 未来走势验证 =====
            double futureAvg = 0;
            for (int j = 1; j <= futureWindow; j++) {
                futureAvg += klines.get(i + j).getClose();
            }
            futureAvg /= futureWindow;

            boolean correctSignal = (executedSignal.equals("buy") && futureAvg > close) ||
                    (executedSignal.equals("sell") && futureAvg < close);

            // 更新统计与状态
            total++;
            if (correctSignal) {
                correct++;
                // 成功则重置连续错误计数，并记录当前执行信号为 lastSignal
                lastSignal = executedSignal;
                consecutiveWrongCount = 0;
            } else {
                // 错误：如果与 lastSignal 相同则 +1，否则重置为1 并更新 lastSignal
                if (executedSignal.equals(lastSignal)) {
                    consecutiveWrongCount++;
                } else {
                    lastSignal = executedSignal;
                    consecutiveWrongCount = 1;
                }
            }

            System.out.printf(
                    "时间:%s, 当前价:%.2f, 区间[%.2f~%.2f], 中间:%.2f, 原始信号:%s, 执行信号:%s%s, 未来均价:%.2f → %s, 连续错次数:%d%n",
                    k.getDatetime(), close, lowest, highest, mid,
                    signal,
                    executedSignal,
                    flipped ? " (flipped)" : "",
                    futureAvg,
                    correctSignal ? "✅正确" : "❌错误",
                    consecutiveWrongCount
            );
        }

        double accuracy = total > 0 ? (correct * 100.0 / total) : 0;
        System.out.printf("区间策略信号准确率: %.2f%% (%d/%d)%n", accuracy, correct, total);
    }
    public static void backtestEMAFullCapital(List<com.managertrade.dto.vo.KlineWithIndicators> klines, int futureWindow) {
        if (klines.size() < 120) {
            System.out.println("K线数据太少，无法回测");
            return;
        }

        int rangePeriod = 100;
        double capital = 10000.0;
        double position = 0.0;
        double entryPrice = 0.0;

        double peak = capital;       // 历史最高本金
        double maxDrawdown = 0.0;    // 最大回撤

        for (int i = rangePeriod; i < klines.size() - futureWindow; i++) {
            com.managertrade.dto.vo.KlineWithIndicators k = klines.get(i);

            List<com.managertrade.dto.vo.KlineWithIndicators> rangeList = klines.subList(i - rangePeriod, i);
            double highest = rangeList.stream().mapToDouble(com.managertrade.dto.vo.KlineWithIndicators::getHigh).max().orElse(0);
            double lowest = rangeList.stream().mapToDouble(com.managertrade.dto.vo.KlineWithIndicators::getLow).min().orElse(0);
            double mid = (highest + lowest) / 2.0;
            double close = k.getClose();

            String signal;
            if (close > highest) {
                signal = "buy";
            } else if (close < lowest) {
                signal = "sell";
            } else if (close >= mid && close <= highest) {
                signal = "hold";
            } else {
                signal = "buy";
            }

            if (signal.equals("hold")) continue;

            // 平仓旧仓
            if (position != 0) {
                double pnl = position * (close - entryPrice);
                capital += pnl;
                position = 0;
            }

            // 全仓开新仓
            position = signal.equals("buy") ? capital / close : -capital / close;
            entryPrice = close;

            // 更新最大回撤
            if (capital > peak) peak = capital;
            double drawdown = (peak - capital) / peak;
            if (drawdown > maxDrawdown) maxDrawdown = drawdown;
        }

        // 最后持仓平掉
        if (position != 0) {
            double lastClose = klines.get(klines.size() - 1).getClose();
            double pnl = position * (lastClose - entryPrice);
            capital += pnl;
            position = 0;

            if (capital > peak) peak = capital;
            double drawdown = (peak - capital) / peak;
            if (drawdown > maxDrawdown) maxDrawdown = drawdown;
        }

        System.out.printf("最终本金: %.2f, 最大回撤: %.2f%%\n", capital, maxDrawdown * 100);
    }



    private static double averageClose(List<com.managertrade.dto.vo.KlineWithIndicators> list) {
        double sum = 0;
        for (com.managertrade.dto.vo.KlineWithIndicators k : list) {
            sum += k.getClose();
        }
        return sum / list.size();
    }
    // ===== 区间策略逻辑 =====
    public static String intervalStrategy(List<com.managertrade.dto.vo.KlineWithIndicators> klines) {
        calculateEMA(klines);
        backtestEMA(klines,22);
        backtestEMAFullCapital(klines,22);
        if (klines == null || klines.size() < 30) return "hold";

        // 最近30根K线作为参考区间
        List<com.managertrade.dto.vo.KlineWithIndicators> recent = klines.subList(klines.size() - 30, klines.size());
        double highest = recent.stream().mapToDouble(com.managertrade.dto.vo.KlineWithIndicators::getHigh).max().orElse(0);
        double lowest = recent.stream().mapToDouble(com.managertrade.dto.vo.KlineWithIndicators::getLow).min().orElse(0);
        double mid = (highest + lowest) / 2;
        double upperMid = (highest + mid) / 2;
        double lowerMid = (mid + lowest) / 2;

        com.managertrade.dto.vo.KlineWithIndicators last = klines.get(klines.size() - 1);
        double price = last.getClose();
        boolean upTrend = last.getEma7() > last.getEma21();

        String action;

        // ===== 策略逻辑 =====
        if (price > highest) {
            // 区间上破
            if (upTrend) {
                action = "buy";  // 突破做多
            } else {
                action = "hold"; // 假突破，观望
            }
        } else if (price < lowest) {
            // 区间下破
            if (!upTrend) {
                action = "sell"; // 跌破做空
            } else {
                action = "hold"; // 假跌破，观望
            }
        } else if (price >= upperMid && price <= highest) {
            // 区间上部
            action = upTrend ? "hold" : "sell";
        } else if (price <= lowerMid && price >= lowest) {
            // 区间下部
            action = upTrend ? "buy" : "hold";
        } else {
            // 中间区域
            action = "hold";
        }

        // ===== 附加过滤条件 =====
        // 如果区间过窄（波动<0.5%），直接忽略
        if ((highest - lowest) / mid < 0.005) {
            action = "hold";
        }

        String datetime = klines.get(klines.size() - 1).getDatetime();
        System.out.printf("预测时间："+datetime+"价格区间: [%.2f ~ %.2f], 当前价: %.2f, EMA趋势: %s → 动作: %s%n",
                lowest, highest, price, upTrend ? "上升" : "下降", action);

        return action;
    }

    /**
     * 将K线转换为资金权益曲线
     */
    public static List<BigDecimal> generateFundsCurve(List<com.managertrade.dto.vo.KlineWithIndicators> klines) {
        List<BigDecimal> fundsList = new ArrayList<>();
        BigDecimal capital = INIT_FUNDS;

        for (int i = 0; i < klines.size() - 1; i++) {
            com.managertrade.dto.vo.KlineWithIndicators curr = klines.get(i);
            com.managertrade.dto.vo.KlineWithIndicators next = klines.get(i + 1);

            double position = curr.getClose() > curr.getOpen() ? 1.0 : -1.0; // 多 or 空
            double diff = (next.getClose() - curr.getClose()) * position;
            capital = capital.add(BigDecimal.valueOf(diff)); // 按价格差更新资金

            fundsList.add(capital);
        }

        return fundsList;
    }

    /** 用于结果接收 */
    static class Result {
        double totalReturn;
        double accuracy;

        public Result(double totalReturn, double accuracy) {
            this.totalReturn = totalReturn;
            this.accuracy = accuracy;
        }
    }
}
